
package com.hlkj.pay.app.merchant.ext.impl.alipay;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import java.math.BigDecimal;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import com.alipay.api.AlipayClient;
import com.alipay.api.domain.AlipayOpenAuthTokenAppModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.response.AlipayOpenAuthTokenAppResponse;
import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.framework.extension.annotation.Extension;
import com.hlkj.framework.extension.executor.ExtensionExecutor;
import com.hlkj.framework.extension.scenario.BizScenario;
import com.hlkj.pay.app.merchant.ext.ChannelMerchantOrderExtPt;
import com.hlkj.pay.app.merchant.ext.MerchantExtConstant;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelPayOrderQueryContext;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelRefundPayOrderContext;
import com.hlkj.pay.app.merchant.ext.impl.alipay.constant.AliPayExtConstant;
import com.hlkj.pay.app.merchant.ext.impl.alipay.constant.XmlUtils;
import com.hlkj.pay.app.merchant.ext.impl.alipay.dto.AlipayAuthNotifyBizContextDto;
import com.hlkj.pay.app.merchant.ext.impl.alipay.dto.AlipayConfig;
import com.hlkj.pay.app.merchant.ext.impl.alipay.handler.AliPayCommonParamsHandle;
import com.hlkj.pay.common.MerchantResultCode;
import com.hlkj.pay.dto.order.PayOrderChannelResultDto;
import com.hlkj.pay.dto.order.notify.CsOrderNotifyDto;
import com.hlkj.pay.dto.order.notify.RefundOrderNotifyDto;
import com.hlkj.pay.enums.PayEnums;
import com.hlkj.pay.enums.PayOrderEnums;
import com.hlkj.pay.infrastructure.model.order.PayOrderDO;
import com.hlkj.pay.infrastructure.model.order.RefundOrderDO;
import com.hlkj.pay.infrastructure.model.pay.PayWayDO;
import com.hlkj.pay.infrastructure.model.pay.ProviderChannelConfigDO;
import com.hlkj.pay.util.DateUtils;
import com.hlkj.pay.vo.order.req.PayOrderCommonParamReq;
import com.ijpay.alipay.AliPayApi;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 支付宝订单操作
 * 
 * @author HlpayTeam
 * @date 2024/10/10 13:51
 */
@Slf4j
@Repository
@RequiredArgsConstructor
@Extension(bizId = MerchantExtConstant.CHANNEL_ALI_PAY)
public class AlipayMerchantOrderExtPt implements ChannelMerchantOrderExtPt {

    private final ExtensionExecutor extensionExecutor;

    private final AliPayCommonParamsHandle aliPayCommonParamsHandle;

    @Override
    public CommonResult<String> channelOrderCode(ChannelPayOrderContext channelPayOrderContext) {
        return CommonResult.success();
    }

    @Override
    public void submitChannelPayOrder(ChannelPayOrderContext channelPayOrderContext) {
        PayWayDO payWayDO = channelPayOrderContext.getPayWayDO();
        // 是否普通商户
        boolean commonMer = aliPayCommonParamsHandle.commonMer(channelPayOrderContext.getMerchantAppChannelDO());
        // 服务商只支持扫码支付
        String aliPay = aliPay(payWayDO,commonMer);
        if (!StringUtils.hasText(aliPay)) {
            channelPayOrderContext.setRemark("当前支付方式不支持");
            channelPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(AlipayScenePayOrderExtPt.class, BizScenario.valueOf(aliPay), ex -> ex.submitChannelPayOrder(channelPayOrderContext));
    }

    @Override
    public void queryChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        // 是否普通商户
        boolean commonMer = aliPayCommonParamsHandle.commonMer(channelPayOrderQueryContext.getMerchantAppChannelDO());
        // 服务商只支持扫码支付
        String aliPay = aliPay(payWayDO,commonMer);
        if (!StringUtils.hasText(aliPay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(AlipayScenePayOrderExtPt.class, BizScenario.valueOf(aliPay), ex -> ex.queryChannelPayOrder(channelPayOrderQueryContext));
    }

    @Override
    public void closeChannelPayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        // 是否普通商户
        boolean commonMer = aliPayCommonParamsHandle.commonMer(channelPayOrderQueryContext.getMerchantAppChannelDO());
        // 服务商只支持扫码支付
        String aliPay = aliPay(payWayDO,commonMer);
        if (!StringUtils.hasText(aliPay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(AlipayScenePayOrderExtPt.class, BizScenario.valueOf(aliPay), ex -> ex.closeChannelPayOrder(channelPayOrderQueryContext));
    }

    @Override
    public void queryChannelClosePayOrder(ChannelPayOrderQueryContext channelPayOrderQueryContext) {
        PayWayDO payWayDO = channelPayOrderQueryContext.getPayWayDO();
        // 是否普通商户
        boolean commonMer = aliPayCommonParamsHandle.commonMer(channelPayOrderQueryContext.getMerchantAppChannelDO());
        // 服务商只支持扫码支付
        String aliPay = aliPay(payWayDO,commonMer);
        if (!StringUtils.hasText(aliPay)) {
            channelPayOrderQueryContext.setRemark("当前支付方式不支持");
            channelPayOrderQueryContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(AlipayScenePayOrderExtPt.class, BizScenario.valueOf(aliPay), ex -> ex.queryChannelClosePayOrder(channelPayOrderQueryContext));

    }

    @Override
    public boolean refundApply(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        return true;
    }

    @Override
    public void refundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        // 是否普通商户
        boolean commonMer = aliPayCommonParamsHandle.commonMer(channelRefundPayOrderContext.getMerchantAppChannelDO());
        // 服务商只支持扫码支付
        String aliPay = aliPay(payWayDO,commonMer);
        if (!StringUtils.hasText(aliPay)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(AlipayScenePayOrderExtPt.class, BizScenario.valueOf(aliPay), ex -> ex.refundChannelPayOrder(channelRefundPayOrderContext));
    }

    @Override
    public void queryRefundChannelPayOrder(ChannelRefundPayOrderContext channelRefundPayOrderContext) {
        PayWayDO payWayDO = channelRefundPayOrderContext.getPayWayDO();
        // 是否普通商户
        boolean commonMer = aliPayCommonParamsHandle.commonMer(channelRefundPayOrderContext.getMerchantAppChannelDO());
        // 服务商只支持扫码支付
        String aliPay = aliPay(payWayDO,commonMer);
        if (!StringUtils.hasText(aliPay)) {
            channelRefundPayOrderContext.setRemark("当前支付方式不支持");
            channelRefundPayOrderContext.setProcessStatus(Boolean.FALSE);
            return;
        }
        extensionExecutor.executeVoid(AlipayScenePayOrderExtPt.class, BizScenario.valueOf(aliPay), ex -> ex.queryRefundChannelPayOrder(channelRefundPayOrderContext));
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> orderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {

        CommonResult<PayOrderChannelResultDto> payOrderDOCommonResult = CommonResult.success();
        try {
            // 支付宝配置信息
            CommonResult<AlipayConfig> alipayConfigCommonResult = aliPayCommonParamsHandle.alipayConfig(providerChannelConfigDO.getParamsConfig());
            if(!alipayConfigCommonResult.isSuccess()){
                log.info("支付宝初始化配置失败,errMsg:{}", alipayConfigCommonResult.getMsg());
                payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
                return payOrderDOCommonResult;
            }
            AlipayConfig alipayConfig = alipayConfigCommonResult.getData();
            // 获取支付宝POST过来反馈信息
            Map<String, String> params = AliPayApi.toMap(request);
            log.info("支付宝支付通知明文 {}", JSONUtil.toJsonStr(params));
            boolean verifyResult = AlipaySignature.rsaCheckV1(params, alipayConfig.getPublicKey(), "UTF-8", "RSA2");
            if(!verifyResult){
                log.info("支付宝回调验签不通过");
                payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
                return payOrderDOCommonResult;
            }

            PayOrderDO updartePayOrderDO = new PayOrderDO();
            String out_trade_no = MapUtil.getStr(params,"out_trade_no");
            updartePayOrderDO.setPayOrderNo(out_trade_no);
            // 交易状态
            String trade_status = MapUtil.getStr(params,"trade_status");
            if(StrUtil.isNotEmpty(trade_status)){
                switch (trade_status) {
                    case "WAIT_BUYER_PAY":
                        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.PAYING.getCode());
                        updartePayOrderDO.setBankMsg("");
                        break;
                    case "TRADE_SUCCESS":
                        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.SUCCESS.getCode());
                        updartePayOrderDO.setBankMsg("");
                        break;
                    case "TRADE_FINISHED":
                    case "TRADE_CLOSED":
                        updartePayOrderDO.setState(PayOrderEnums.PAY_ORDER_STATUS.CLOSE.getCode());
                        break;
                }
            }

            // 付款时间
            String gmt_payment = MapUtil.getStr(params,"gmt_payment");
            if(StrUtil.isNotEmpty(gmt_payment)){
                updartePayOrderDO.setSuccessTime(DateUtils.shanghaiDate(gmt_payment, DatePattern.NORM_DATETIME_PATTERN));
            }
            // 支付宝交易号
            String trade_no = MapUtil.getStr(params,"trade_no");
            if(StrUtil.isNotEmpty(trade_no)){
                updartePayOrderDO.setChannelOrderNo(trade_no);
            }
            // 订单金额
            String total_amount = MapUtil.getStr(params,"total_amount");
            if(StrUtil.isNotEmpty(total_amount)){
                updartePayOrderDO.setPayAmount(new BigDecimal(total_amount));
            }
            // 实收金额
            String receipt_amount = MapUtil.getStr(params,"receipt_amount");
            if(StrUtil.isNotEmpty(receipt_amount)){
                updartePayOrderDO.setSettlementAmount(new BigDecimal(receipt_amount));
            }
            // 用户ID
            String buyer_id = MapUtil.getStr(params,"buyer_id");
            if(StrUtil.isNotEmpty(buyer_id)){
                updartePayOrderDO.setUserId(buyer_id);
            }
            PayOrderChannelResultDto payOrderChannelResultDto = new PayOrderChannelResultDto();
            payOrderChannelResultDto.setUpdatePayOrderDO(updartePayOrderDO);
            payOrderChannelResultDto.setCallBackBody(JSONUtil.toJsonStr(params));
            payOrderDOCommonResult.setData(payOrderChannelResultDto);
        }
        catch (Exception e) {
            payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
        }
        log.info("支付宝收到回调,解析后的订单数据为 payOrderDO:{}", JsonUtils.toJsonStringNotNull(payOrderDOCommonResult.getData()));
        return payOrderDOCommonResult;
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> orderStatusParse(HttpServletRequest request) {
        return null;
    }

    @Override
    public CommonResult<PayOrderChannelResultDto> merAuthParse(HttpServletRequest request) {

        CommonResult<PayOrderChannelResultDto> payOrderDOCommonResult = CommonResult.success();
        try {
            // {"source":"alipay_app_auth","app_id":"2021005132675376","app_auth_code":"Pc61d9c8e53b44d839cf071c8ca85b13"}
            // 获取支付宝POST过来反馈信息
            Map<String, String> params = AliPayApi.toMap(request);
            log.info("支付宝授权通知内容：{}",JSONUtil.toJsonStr(params));
            if(MapUtil.isEmpty(params)){
                log.info("支付宝授权通知内容为空");
                return payOrderDOCommonResult;
            }

            String source = MapUtil.getStr(params,"source");
            if(StrUtil.isNotEmpty(source)){
                log.info("支付宝授权通知 认证 source ：{}",source);
                if(source.equals("alipay_app_auth")){
                    String app_id = MapUtil.getStr(params,"app_id");
                    String app_auth_code = MapUtil.getStr(params,"app_auth_code");
                    AlipayClient client = aliPayCommonParamsHandle.getAlipaClientByAppid(app_id);
                    if(client != null && StrUtil.isNotEmpty(app_auth_code)){
                        // 获取AlipayClient
                        AlipayOpenAuthTokenAppModel authTokenAppModel = new AlipayOpenAuthTokenAppModel();
                        authTokenAppModel.setCode(app_auth_code);
                        authTokenAppModel.setGrantType("authorization_code");
                        AlipayOpenAuthTokenAppResponse authTokenAppResponse = AliPayApi.openAuthTokenAppToResponse(client,false,authTokenAppModel);
                        // 更新token
                        if(authTokenAppResponse.isSuccess()){
                            aliPayCommonParamsHandle.updateAuthToken(authTokenAppResponse,app_id);
                        }
                    }
                }
            }else{
                String notify_type = MapUtil.getStr(params,"notify_type");
                log.info("支付宝授权通知 回调 notify_type ：{}",notify_type);
                if(StrUtil.isNotEmpty(notify_type) && notify_type.equals("open_app_auth_notify")){
                    String biz_content = MapUtil.getStr(params,"biz_content");
                    if(StrUtil.isNotEmpty(biz_content)){
                        Map<String, String> biz_content_map = XmlUtils.getMapFromXML(biz_content);
                        if(MapUtil.isNotEmpty(biz_content_map)){
                            AlipayAuthNotifyBizContextDto alipayAuthNotifyBizContextDto = JSONUtil.toBean(JSONUtil.toJsonStr(biz_content_map),AlipayAuthNotifyBizContextDto.class);
                            if(alipayAuthNotifyBizContextDto != null && alipayAuthNotifyBizContextDto.getDetail() != null){
                                AlipayAuthNotifyBizContextDto.Detail detail = alipayAuthNotifyBizContextDto.getDetail();
                                AlipayOpenAuthTokenAppResponse authTokenAppResponse = new AlipayOpenAuthTokenAppResponse();
                                authTokenAppResponse.setAppAuthToken(detail.getApp_auth_token());
                                authTokenAppResponse.setAppRefreshToken(detail.getApp_refresh_token());
                                aliPayCommonParamsHandle.updateAuthToken(authTokenAppResponse,detail.getApp_id());
                            }
                        }
                    }
                }
            }
            PayOrderChannelResultDto payOrderChannelResultDto = new PayOrderChannelResultDto();
            payOrderChannelResultDto.setCallBackBody(JSONUtil.toJsonStr(params));
            payOrderDOCommonResult.setData(payOrderChannelResultDto);
        }
        catch (Exception e) {
            log.error("支付宝收到授权通知回调,解析失败 errMsg:{}", e);
        }
        return payOrderDOCommonResult;
    }

    @Override
    public PayOrderDO closeOrderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        return null;
    }

    @Override
    public CommonResult<CsOrderNotifyDto> csOrderStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {
        return null;
    }

    @Override
    public CommonResult<RefundOrderNotifyDto> refundStatusParse(HttpServletRequest request, ProviderChannelConfigDO providerChannelConfigDO) {

        CommonResult<RefundOrderNotifyDto> payOrderDOCommonResult = CommonResult.success();
        try {
            // 支付宝配置信息
            CommonResult<AlipayConfig> alipayConfigCommonResult = aliPayCommonParamsHandle.alipayConfig(providerChannelConfigDO.getParamsConfig());
            if(!alipayConfigCommonResult.isSuccess()){
                log.info("支付宝初始化配置失败,errMsg:{}", alipayConfigCommonResult.getMsg());
                payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
            }
            AlipayConfig alipayConfig = alipayConfigCommonResult.getData();
            // 获取支付宝POST过来反馈信息
            Map<String, String> params = AliPayApi.toMap(request);
            log.info("支付宝退款通知明文 {}", JSONUtil.toJsonStr(params));
            boolean verifyResult = AlipaySignature.rsaCheckV1(params, alipayConfig.getPublicKey(), "UTF-8", "RSA2");
            if(!verifyResult){
                log.info("支付宝回调验签不通过");
                payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
            }
            RefundOrderNotifyDto refundOrderNotifyDto=new RefundOrderNotifyDto();
            refundOrderNotifyDto.setCallBackBody(JSONUtil.toJsonStr(params));
            RefundOrderDO updateRefundOrderDO = new RefundOrderDO();

            // 退款状态
            String trade_status = MapUtil.getStr(params,"trade_status");
            if(StrUtil.isNotEmpty(trade_status)){
                switch (trade_status) {
                    case "SUCCESS":
                        updateRefundOrderDO.setRefundStatus(PayOrderEnums.REFUND_STATUS.REFUND.getCode());
                        updateRefundOrderDO.setBankMsg("");
                        // 实际退款金额
                        String send_back_fee = MapUtil.getStr(params,"send_back_fee");
                        if(StrUtil.isNotEmpty(send_back_fee)){
                            updateRefundOrderDO.setActualRefundAmount(new BigDecimal(send_back_fee));
                        }
                        // 退款成功时间
                        String gmt_refund = MapUtil.getStr(params,"gmt_refund");
                        if(StrUtil.isNotEmpty(gmt_refund)){
                            updateRefundOrderDO.setSuccessTime(DateUtils.shanghaiDate(gmt_refund, DatePattern.NORM_DATETIME_PATTERN));
                        }
                        refundOrderNotifyDto.setRefundOrder(updateRefundOrderDO);
                        payOrderDOCommonResult.setData(refundOrderNotifyDto);
                        break;
                }
            }
        }
        catch (Exception e) {
            payOrderDOCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
        }

        log.info("支付宝收到退款回调,解析后的订单数据为 payOrderDO:{}", JsonUtils.toJsonStringNotNull(payOrderDOCommonResult.getData()));
        return payOrderDOCommonResult;
    }

    @Override
    public String channelPayCode(PayWayDO payWayDO) {
        return aliPay(payWayDO,false);
    }

    @Override
    public Long queryChannelPayAppid(ChannelPayOrderContext channelPayOrderContext) {
        PayWayDO payWayDO = channelPayOrderContext.getPayWayDO();
        PayOrderDO payOrderDO = channelPayOrderContext.getPayOrderDO();
        PayOrderCommonParamReq.Extra extra = channelPayOrderContext.getExtra();
        if (extra == null && StringUtils.hasText(payOrderDO.getExtra())) {
            extra = JsonUtils.parseObject(payOrderDO.getExtra(), PayOrderCommonParamReq.Extra.class);
        }
        PayEnums.PAY_TYPE payType = PayEnums.PAY_TYPE.fromByCode(payWayDO.getType());
        PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.fromByType(payWayDO.getSubType());
        ProviderChannelConfigDO providerChannelConfigDO = channelPayOrderContext.getProviderChannelConfigDO();
        if (extra != null && payType.getCode().equals(PayEnums.PAY_TYPE.WECHAT.getCode())) {
            switch (scenePayType) {
                case QR:
                case APP:
                case H5:
                case JSAPI:
                    return providerChannelConfigDO.getWechatDefaultPubId();
                case MINI_APP:
                    return providerChannelConfigDO.getWechatDefaultMiniId();
                default:
            }
        }

        return null;
    }
    @Override
    public String callBackResponse(CommonResult commonResult) {
        if (commonResult.isSuccess()) {
            return "success";
        }
        else {
            return "fail";
        }
    }

    @Override
    public boolean wechatLink(ChannelPayOrderContext channelPayOrderContext) {
        return false;
    }

    /**
     * 支付宝支付方式路由
     * 
     * @param payWayDO
     * @return
     */
    private String aliPay(PayWayDO payWayDO,boolean commonMer) {
        PayEnums.SCENE_PAY_TYPE scenePayType = PayEnums.SCENE_PAY_TYPE.fromByCode(payWayDO.getCode());
        if (scenePayType == null) {
            return null;
        }

        if(commonMer){
            switch (scenePayType) {
                case PC:
                    return AliPayExtConstant.PC;
                case APP:
                    return AliPayExtConstant.APP_PAY;
                case WAP:
                    return AliPayExtConstant.WAP;
                case QR:
                    return AliPayExtConstant.NATIVE_PAY;
            }
        }else{
            switch (scenePayType) {
                case QR:
                    return AliPayExtConstant.NATIVE_PAY;
            }
        }
        return null;
    }
}
